package cn.edu.neu.vmmigeval.cloud.policy.impl;


import cn.edu.neu.vmmigeval.cloud.config.Migration;
import cn.edu.neu.vmmigeval.cloud.entity.Host;
import cn.edu.neu.vmmigeval.cloud.entity.Vm;
import cn.edu.neu.vmmigeval.cloud.policy.MigratedHostPolicy;
import cn.edu.neu.vmmigeval.cloud.policy.VmPlacementPolicy;
import cn.edu.neu.vmmigeval.evaluation.PowerModel;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class VmPlacementPolicyGreedyHeuristic extends VmPlacementPolicy {

	public VmPlacementPolicyGreedyHeuristic(List<Host> hostList,
			MigratedHostPolicy migratedHostPolicy) {
		super(hostList, migratedHostPolicy);
	}
	
	
	public VmPlacementPolicyGreedyHeuristic(
			MigratedHostPolicy migratedHostPolicy) {
		super( migratedHostPolicy);
	}
	
	public VmPlacementPolicyGreedyHeuristic(){
		
	}

	public List<Migration> getNewVmPlacement(List<Vm> vmsToMigrate, List<Host> hosts) {
		//calculateVmSortingFactor(vmsToMigrate);
		//List<Vm> newVms = sortVmBySortingFactor(vmsToMigrate);
		
		///
		List<Migration> migs = new ArrayList<Migration>();
		for (Vm vm : vmsToMigrate) {
			Host minHost = getMinDeltaPowerHost(getExcludedHosts(hosts),vm);
			//Host targetHost = findHostForVm(vm, getExcludedHosts(hosts),threshold);
			if (minHost != null) {
				
				Host srcHost = vm.getHost();
				vm.setInMigration(true);
				List<Vm> vms = new ArrayList<Vm>();
				vms.add(vm);
				Migration migration = new Migration();
				migration.setSrcHost(srcHost);
				migration.setDestHost(minHost);
				migration.setVms(vms);
				/*if(srcHost.getTotalBw()-srcHost.getCurrentBw()<=0){
					System.out.println("nima");
				}*/
				migration.setClocks((int)((vm.getCurrentRam()/(minHost.getTotalBw()-minHost.getCurrentBw()+10)))+1);
				//System.out.println("clocks is :"+migration.getClocks());
				migration.setEnd(false);
				
				srcHost.setMigration(true);
				minHost.setMigration(true);
				minHost.setActive(0);
				migs.add(migration);
			}
		}
		return migs;
		
		///
		
	/*	List<Migration>  migrationList = new LinkedList<Migration>();
		for(Vm vm : newVms){
			List<Host> sortHost = new ArrayList<Host>();
			for(Host host : hosts){
				if(host.isSuitableForVm(vm)){
					if (isHostOverUtilizedAfterAllocation(host, vm)) {
						continue;
					}
					host.additionalPower(vm);
					sortHost.add(host);
					Migration migrate = new Migration();
					vm.setInMigration(true);
					migrate.setSrcHost(vm.getHost());
					migrate.setDestHost(host);
					List<Vm>  vms = new LinkedList<Vm>();
					vms.add(vm);
					migrate.setVms(vms);
					migrationList.add(migrate);
				}
			}
			Host minHost = getMinDeltaPowerHost(sortHost);
			if(minHost!=null){
				Migration migrate = new Migration();
				vm.setInMigration(true);
				migrate.setSrcHost(vm.getHost());
				migrate.setDestHost(minHost);
				List<Vm>  vms = new LinkedList<Vm>();
				vms.add(vm);
				migrate.setVms(vms);
				migrationList.add(migrate);
			}
		}
		return migrationList;
		*/
	}
	
	/**
     * @author zzh
     * @param vmList
     */
	private List<Vm> sortVmBySortingFactor(List<Vm> vmList) {
		
		List<Vm> vms = new ArrayList<Vm>();
		for (Vm vm : vmList) {
			vms.add(vm);
		}
        Collections.sort(vms, new Comparator<Vm>() {
            @Override
            public int compare(Vm a, Vm b) throws ClassCastException {
            	Double aUtilization = Double.valueOf(a.getSortFactor());
                Double bUtilization = Double.valueOf(b.getSortFactor());
                return aUtilization.compareTo(bUtilization);
            }
        });
        return vms;
    }
	
	/**
     * @author zzh
     * @param hosts
     */
	private Host getMinDeltaPowerHost(List<Host> hosts,Vm vm) {
		
		//List<Double> powers = new ArrayList<Double>();
		
		//PowerModel pm = new PowerModel();
		double small= Double.MAX_VALUE;
		//int bigId = 0;
		//int id = 0 ;
		Host h = null;
		for (Host host : hosts) {
			//id = host.getId();
			double power  = PowerModel.getPowerOfHost(0, host);
			/*System.out.println("power is :"+power);
			if(power>Double.MAX_VALUE){
				System.out.println("power is :"+power);
			}*/
			
			if(power<small&&host.isSuitableForVm(vm)){
				small = power;
				//bigId = host.getId();
				h = host;
			}
			
		}
       return h;
    }
	
//	/**
//	 * Checks if is host over utilized after allocation.
//	 *
//	 * @param host the host
//	 * @param vm the vm
//	 * @return true, if is host over utilized after allocation
//	 */
	/*protected boolean isHostOverUtilizedAfterAllocation(Host host, Vm vm) {
		boolean isHostOverUtilizedAfterAllocation = true;
		if (host.vmCreate(vm)) {
			
			MigratedHostPolicyDefault mhpd = new MigratedHostPolicyDefault();
			
			isHostOverUtilizedAfterAllocation = mhpd.isHostOverUtilized(host);//this.getMigratedHostPolicy().isHostOverUtilized(host);
			host.vmDestroy(vm);
		}
		return isHostOverUtilizedAfterAllocation;
	}*/
	
	public void calculateVmSortingFactor(List<Vm> vmList){
		double totalCpu = 0;
		double totalRam = 0;
		double totalBw = 0;
		double totalDisk = 0;
		for(Vm vm : vmList){
			totalCpu += vm.getTotalCpu();
			totalRam += vm.getTotalRam();
			totalBw += vm.getCurrentBw();
			totalDisk += vm.getCurrentDisk();
		}
		
		double factor = 0;
		for(Vm vm : vmList){
			factor = vm.getTotalCpu()/totalCpu*4 
					+ vm.getTotalRam()/totalRam
					+ vm.getCurrentBw()/totalBw
					+ vm.getCurrentDisk()/totalDisk;
			vm.setSortFactor(factor);
		}
	}
	
	public void calculateHostSortingFactor(List<Host> hosts){
		for(Host host : hosts){
			double fmax = 1.0;
			double sort = host.getTotalCpu()*4+host.getTotalRam() + host.getTotalBw()+host.getTotalDisk();;
			host.setSortingFactor(sort);
		}
	}
}
